package com.pro01.apirednote.aop;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pro01.apirednote.pojo.Result;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class LoggingAspect {

    private final ObjectMapper objectMapper;
    private final PerformanceMetricsCollector metricsCollector;

    @Pointcut("execution(* com.pro01.apirednote.controller.*.*(..))")
    public void controllerMethods() {}

    @Around("controllerMethods()")
    public Object logAroundControllers(ProceedingJoinPoint joinPoint) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestURI = request.getRequestURI();
        String method = request.getMethod();

        LocalDateTime startTime = LocalDateTime.now();
        String formattedStartTime = startTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();

        Object[] args = joinPoint.getArgs();
        Map<String, Object> requestParams = new HashMap<>();

        if (args != null && args.length > 0) {
            for (int i = 0; i < signature.getParameterNames().length; i++) {
                String paramName = signature.getParameterNames()[i];
                Object arg = args[i];

                if (arg instanceof MultipartFile multipartFile) {
                    requestParams.put(paramName, "文件名: " + multipartFile.getOriginalFilename() +
                            ", 大小: " + multipartFile.getSize() + " 字节");
                } else if (arg instanceof MultipartFile[] multipartFiles) {
                    List<String> fileInfos = new ArrayList<>();
                    for (MultipartFile f : multipartFiles) {
                        fileInfos.add(f.getOriginalFilename() + " (" + f.getSize() + " 字节)");
                    }
                    requestParams.put(paramName, fileInfos);
                } else if (arg instanceof HttpServletRequest) {
                    continue;
                } else {
                    requestParams.put(paramName, arg);
                }
            }
        }

        try {
            String paramsJson = objectMapper.writeValueAsString(requestParams);
            log.info("请求开始 - URI: {}, 方法: {}, 控制器方法: {}, 开始时间: {}, 请求参数: {}",
                    requestURI, method, methodName, formattedStartTime, paramsJson);
        } catch (Exception e) {
            log.warn("请求参数序列化失败 - URI: {}, 参数被简化展示", requestURI);
        }

        long startMillis = System.currentTimeMillis();
        Object result = null;

        try {
            result = joinPoint.proceed();

            int statusCode = 200;
            if (result instanceof Result<?> r) {
                statusCode = r.getCode();
            }
            metricsCollector.incrementRequestCount(requestURI, method, statusCode);

            return result;
        } catch (Throwable e) {
            log.error("请求异常 - URI: {}, 方法: {}, 控制器方法: {}, 异常信息: {}",
                    requestURI, method, methodName, e.getMessage(), e);
            metricsCollector.incrementExceptionCount(requestURI, method, e.getClass().getSimpleName());
            throw e;
        } finally {
            long executionTime = System.currentTimeMillis() - startMillis;
            metricsCollector.recordExecutionTime(requestURI, method, executionTime);

            String resultJson;
            try {
                resultJson = objectMapper.writeValueAsString(result);
                if (resultJson.length() > 1000) {
                    resultJson = resultJson.substring(0, 1000) + "... (已截断)";
                }
            } catch (Exception e) {
                resultJson = "无法序列化的返回值";
            }

            log.info("请求结束 - URI: {}, 方法: {}, 控制器方法: {}, 耗时: {}ms, 返回值: {}",
                    requestURI, method, methodName, executionTime, resultJson);

            if (executionTime > 1000) {
                log.warn("性能警告 - URI: {}, 方法: {}, 控制器方法: {}, 耗时: {}ms (超过1秒)",
                        requestURI, method, methodName, executionTime);
            }
        }
    }
}